﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MyTRCP.Common.Models;
using Org.BouncyCastle.Crypto;

namespace MyTRCP.Common.Services
{
    public class DevCmdService : BackgroundService
    {
        private class CmdInfo
        {
            public DataPayload RequestCmd { get; private set; }
            public ResponsePayload? RespResult { get; set; }
            public CmdInfo(DataPayload reqCmd)
            {
                RequestCmd = reqCmd;
            }
        }
        private readonly DevCtrlLinkService? _dcl;
        private readonly MyProtocolClientSideService? _mpcs;
        private readonly BlockingQueue<byte[]> _recvQ;
        private readonly BlockingQueue<byte[]> _sendQ;
        public event EventHandler<NotificationReceivedEventArgs>? NotificationReceived;
        public event EventHandler<DevSettings>? DevSettingsChanged;
        public event EventHandler<DevStatus>? DevStatusChanged;
        public event EventHandler<TimeSpan>? NewCmdLatencyComputed;

        private readonly Dictionary<AutoResetEvent, CmdInfo> _waitResponseAREDic;

        private readonly ILogger<DevCmdService> _logger;
        private bool IsCtrlLinkConnected
        {
            get
            {
                if (_dcl != null)
                {
                    return _dcl.IsConnected;
                }
                else if (_mpcs != null)
                {
                    return _mpcs.IsCtrlConnected;
                }
                else
                {
                    return false;
                }
            }
        }
        public DevCmdService(ILogger<DevCmdService> logger, IConfigService ics, DevCtrlLinkService idcls, MyProtocolClientSideService impcs)
        {
            logger.LogDebug($"DevCmdService creating...");
            _logger = logger;
            if (ics is ClientConfigService ccs && ccs.ClientMode == ClientModeEnum.Remote)
            {
                _mpcs = impcs;
                _recvQ = _mpcs.CtrlRxQueue;
                _sendQ = _mpcs.CtrlTxQueue;
            }
            else
            {
                _dcl = idcls;
                _recvQ = _dcl.PayloadRecvQueue;
                _sendQ = _dcl.PayloadSendQueue;
            }
            _waitResponseAREDic = new Dictionary<AutoResetEvent, CmdInfo>();
        }
        public override Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogDebug("startasync called");
            return base.StartAsync(cancellationToken);
        }
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await Task.Run(() =>
            {
                RecvProcessThreadMethod(stoppingToken);
            }, stoppingToken);
        }
        private void SendWithoutResponse(DataPayload reqPl)
        {
            if (!IsCtrlLinkConnected)
                throw new InvalidOperationException("未连接设备，无法发送");
            _sendQ.Add(DataPayload.Pack(reqPl));
        }
        private ResponsePayload SendWithResponse(DataPayload reqPl)
        {
            if (!IsCtrlLinkConnected)
                throw new InvalidOperationException("未连接设备，无法发送");
            AutoResetEvent are = new AutoResetEvent(false);
            _waitResponseAREDic.Add(are, new CmdInfo(reqPl));
            DateTime startTime = DateTime.Now;
            _sendQ.Add(DataPayload.Pack(reqPl));
            if (are.WaitOne(5000))
            {
                NewCmdLatencyComputed?.Invoke(this, DateTime.Now - startTime);
                if (_waitResponseAREDic.TryGetValue(are, out var value))
                {
                    ResponsePayload? dp = value.RespResult;
                    _waitResponseAREDic.Remove(are);
                    if (dp != null)
                    {
                        if (dp.ErrorCodeEnum == RespErrorCodeEnum.SUCCESS)
                        {
                            return dp;
                        }
                        else
                        {
                            throw new DevCmdException($"设备返回错误码:{dp.ErrorCodeEnum}");
                        }
                    }
                    else
                    {
                        throw new DevCmdException("未收到返回数据包");
                    }
                }
                else
                {
                    throw new DevCmdException("返回包丢失");
                }
            }
            else
            {
                _waitResponseAREDic.Remove(are);
                NewCmdLatencyComputed?.Invoke(this, DateTime.Now - startTime);
                if (IsCtrlLinkConnected)
                {
                    throw new TimeoutException("获取返回值超时");
                }
                else
                {
                    throw new InvalidOperationException("已断开连接");
                }
            }
        }
        private static DataPayload GenerateRequestPayload(CmdIdEnum cmd, params byte[] args)
        {
            return new DataPayload(2, (ushort)cmd, false, args);
        }

        public async Task RegisterNotification(NotificationEventTypeEnum type)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.REGISTER_NOTIFICATION, (byte)type);
                SendWithoutResponse(reqPl);
            });
        }
        public async Task CancelNotification(NotificationEventTypeEnum type)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.CANCEL_NOTIFICATION, (byte)type);
                SendWithoutResponse(reqPl);
            });
        }
        public async Task<DevInfoType3> GetDevInfoType3()
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.GET_DEV_INFO, 0x3);
                ResponsePayload rp = SendWithResponse(reqPl);
                return new DevInfoType3(rp.Datas);
            });
        }
        public async Task<VolumeInfo> GetVolume()
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.GET_VOLUME);
                ResponsePayload rp = SendWithResponse(reqPl);
                return new VolumeInfo(rp.Datas);
            });
        }
        public async Task<DevSettings> ReadSettings()
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.READ_SETTINGS);
                ResponsePayload rp = SendWithResponse(reqPl);
                return new DevSettings(rp.Datas);
            });
        }
        public async Task<ChannelPayload> ReadRfCh(byte id)
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.READ_RF_CH, id);
                ResponsePayload rp = SendWithResponse(reqPl);
                return new ChannelPayload(rp.Datas);
            });
        }
        public async Task<byte> WriteRfCh(ChannelPayload cp)
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.WRITE_RF_CH, cp.WriteToBytes());
                ResponsePayload rp = SendWithResponse(reqPl);
                return rp.Datas[0];
            });
        }
        public async Task<RegionNamePayload> ReadRegionName(byte id)
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.READ_REGION_NAME, id);
                ResponsePayload rp = SendWithResponse(reqPl);
                return new RegionNamePayload(rp.Datas);
            });
        }
        public async Task SetVolume(byte vol)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.SET_VOLUME, vol);
                ResponsePayload rp = SendWithResponse(reqPl);
            });
        }
        public async Task<BssSettings> ReadBssSettings()
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.READ_BSS_SETTINGS, 0x2);
                ResponsePayload rp = SendWithResponse(reqPl);
                return new BssSettings(rp.Datas);
            });
        }
        public async Task<ushort> ReadStatusBatteryVoltage()
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.READ_STATUS, Tools.Int16ToBEBA((short)StatusTypeEnum.BATTERY_VOLTAGE));
                ResponsePayload rp = SendWithResponse(reqPl);
                StatusPayload sp = new StatusPayload(rp.Datas);
                return Tools.UInt16FromBEBA(sp.Data);
            });
        }
        public async Task<byte> ReadStatusBatteryLevelAsPercentage()
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.READ_STATUS, Tools.Int16ToBEBA((short)StatusTypeEnum.BATTERY_LEVEL_AS_PERCENTAGE));
                ResponsePayload rp = SendWithResponse(reqPl);
                StatusPayload sp = new StatusPayload(rp.Datas);
                return sp.Data[0];
            });
        }
        public async Task SetIsDigitalSignal(bool isDigiSignal)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.SET_IS_DIGITAL_SIGNAL, isDigiSignal ? (byte)1 : (byte)0);
                ResponsePayload rp = SendWithResponse(reqPl);
            });
        }
        public async Task SetRegion(byte regionId)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.SET_REGION, regionId);
                ResponsePayload rp = SendWithResponse(reqPl);
            });
        }
        public async Task WriteSettings(DevSettings settings)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.WRITE_SETTINGS, settings.WriteToBytes());
                ResponsePayload rp = SendWithResponse(reqPl);
            });
        }
        public async Task SetTime(uint timestamp)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.SET_TIME, Tools.UInt32ToBEBA(timestamp));
                ResponsePayload rp = SendWithResponse(reqPl);
            });
        }
        public async Task SetHL(byte id)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.SET_HL, id);
                ResponsePayload rp = SendWithResponse(reqPl);
            });
        }
        public async Task SetPosition(Location loc)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.SET_POSITION, loc.WriteToBytes());
                ResponsePayload rp = SendWithResponse(reqPl);
            });
        }
        public async Task<DevStatus> GetHtStatus()
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.GET_HT_STATUS);
                ResponsePayload rp = SendWithResponse(reqPl);
                return new DevStatus(rp.Datas);
            });
        }
        public async Task<RfStatus> ReadRfStatus()
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.READ_RF_STATUS);
                ResponsePayload rp = SendWithResponse(reqPl);
                return new RfStatus(rp.Datas);
            });
        }
        public async Task WriteRegionCh(RegionChannelPayload rcp)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.WRITE_REGION_CH, rcp.WriteToBytes());
                ResponsePayload rp = SendWithResponse(reqPl);
            });
        }
        public async Task WriteRda1846sAgc(Rda1846sAgc agc)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.WRITE_RDA1846S_AGC, agc.WriteToBytes());
                ResponsePayload rp = SendWithResponse(reqPl);
            });
        }
        public async Task<Rda1846sAgc> ReadRda1846sAgc()
        {
            return await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.READ_RDA1846S_AGC);
                ResponsePayload rp = SendWithResponse(reqPl);
                return new Rda1846sAgc(rp.Datas);
            });
        }
        public async Task SetHtOnOff(bool onOff)
        {
            await Task.Run(() =>
            {
                DataPayload reqPl = GenerateRequestPayload(CmdIdEnum.SET_HT_ON_OFF, onOff ? (byte)1 : (byte)0);
                ResponsePayload rp = SendWithResponse(reqPl);
            });
        }

        private void RecvProcessThreadMethod(CancellationToken stoppingToken)
        {
            try
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    try
                    {
                        byte[] recvCmdByte = _recvQ.Take();
                        DataPayload respPl = DataPayload.Unpack(recvCmdByte);
                        if (respPl.PayloadType == 0x2)
                        {
                            if (respPl.CommandId == (ushort)CmdIdEnum.EVENT_NOTIFICATION)
                            {
                                if (respPl.Arguments.Length > 0)
                                {
                                    NotificationReceivedEventArgs nrea = new NotificationReceivedEventArgs(respPl.Arguments);
                                    switch (nrea.Type)
                                    {
                                        case NotificationEventTypeEnum.HT_STATUS_CHANGED:
                                            DevStatus dscea = new DevStatus(nrea.Data);
                                            DevStatusChanged?.Invoke(this, dscea);
                                            break;
                                        case NotificationEventTypeEnum.HT_SETTINGS_CHANGED:
                                            DevSettings ds = new DevSettings(nrea.Data);
                                            DevSettingsChanged?.Invoke(this, ds);
                                            break;
                                        default:
                                            NotificationReceived?.Invoke(this, nrea);
                                            break;
                                    }
                                }
                                else
                                {
                                    _logger.LogDebug($"收到的通知的参数长度为0，无法解析");
                                }
                            }
                            else if ((respPl.CommandId & 0x8000) == 0x8000)
                            {
                                ResponsePayload rp = new ResponsePayload(respPl);
                                AutoResetEvent? targetARE = null;
                                foreach (KeyValuePair<AutoResetEvent, CmdInfo> kvp in _waitResponseAREDic)
                                {
                                    CmdIdEnum reqCmd = (CmdIdEnum)kvp.Value.RequestCmd.CommandId;
                                    if (reqCmd == rp.OriginalCmdId)
                                    {
                                        if (rp.ErrorCodeEnum != RespErrorCodeEnum.SUCCESS)
                                        {
                                            kvp.Value.RespResult = rp;
                                            targetARE = kvp.Key;
                                            break;
                                        }
                                        else if (rp.OriginalCmdId == CmdIdEnum.READ_STATUS)
                                        {
                                            if (rp.Datas.Length >= 2 && kvp.Value.RequestCmd.Arguments.Length >= 2 && Tools.Int16FromBEBA(rp.Datas) == Tools.Int16FromBEBA(kvp.Value.RequestCmd.Arguments))
                                            {
                                                kvp.Value.RespResult = rp;
                                                targetARE = kvp.Key;
                                                break;
                                            }
                                        }
                                        else if (rp.OriginalCmdId == CmdIdEnum.READ_RF_CH || rp.OriginalCmdId == CmdIdEnum.WRITE_RF_CH)
                                        {
                                            if (rp.Datas.Length >= 1 && kvp.Value.RequestCmd.Arguments.Length >= 1 && rp.Datas[0] == kvp.Value.RequestCmd.Arguments[0])
                                            {
                                                kvp.Value.RespResult = rp;
                                                targetARE = kvp.Key;
                                                break;
                                            }
                                        }
                                        else
                                        {
                                            kvp.Value.RespResult = rp;
                                            targetARE = kvp.Key;
                                            break;
                                        }
                                    }
                                }
                                if (targetARE != null)
                                {
                                    targetARE?.Set();
                                }
                                else
                                {
                                    _logger.LogDebug($"收到未知命令的响应：payloadType={respPl.PayloadType},cmdId=0x{respPl.CommandId:x4}");
                                }
                            }
                            else
                            {
                                _logger.LogDebug($"收到未知命令：payloadType={respPl.PayloadType},cmdId={respPl.CommandId}");
                            }
                        }
                        else
                        {
                            _logger.LogDebug($"收到其他类型的载荷，payloadType={respPl.PayloadType},cmdId={respPl.CommandId}");
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation("收到退出信号，退出循环");
                        break;
                    }
                    catch (Exception e)
                    {
                        _logger.LogError(e, $"解析收到的命令时出现异常：{e.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogDebug(ex, $"命令接收处理线程出现异常将退出：{ex.Message}");
            }
        }
        public override Task StopAsync(CancellationToken cancellationToken)
        {
            _recvQ.CancelTake();
            return base.StopAsync(cancellationToken);
        }
    }
}
